﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.Statistics.Distribution
{
    /// <summary>
    /// This class represents a F-distribution.
    /// </summary>
    [Serializable]
    public class FDistribution
    {
        /// <summary>
        /// The first parameter of the distribution d1.
        /// </summary>
        private readonly uint d1;

        /// <summary>
        /// The second parameter of the distribution d2.
        /// </summary>
        private readonly uint d2;

        /// <summary>
        /// Initializes a new instance of the <see cref="FDistribution"/> class.
        /// </summary>
        /// <param name="d1">The first parameter of the distribution D1.</param>
        /// <param name="d2">The second parameter of the distribution D2.</param>
        public FDistribution(uint d1, uint d2)
        {
            this.d1 = d1;
            this.d2 = d2;
        }

        /// <summary>
        /// Gets the first parameter of the distribution D1.
        /// </summary>
        /// <value>The first parameter of the distribution D1.</value>
        public uint D1
        {
            get { return this.d1; }
        }

        /// <summary>
        /// Gets the second parameter of the distribution D2.
        /// </summary>
        /// <value>The second parameter of the distribution D2.</value>
        public uint D2
        {
            get { return this.d2; }
        }

        /// <summary>
        /// Gets the mean of the distribution.
        /// </summary>
        /// <value>The mean of the distribution.</value>
        public double Mean
        {
            get
            {
                if (this.d2 > 2)
                {
                    return (double) this.d2/(this.d2 - 2);
                }

                return 0;
            }
        }

        /// <summary>
        /// Gets the mode of the distribution.
        /// </summary>
        /// <value>The mode of the distribution.</value>
        public double Mode
        {
            get
            {
                if (this.d1 > 1)
                {
                    return ((this.d1 - 2)/this.d1)*(this.d2/(this.d2 + 2));
                }

                return 0;
            }
        }

        /// <summary>
        /// Gets the variance of the distribution.
        /// </summary>
        /// <value>The variance of the distribution.</value>
        public double Variance
        {
            get
            {
                if (this.d2 > 4)
                {
                    return (Math.Pow(this.d2, 2)*(this.d1 + this.d2 - 2))/
                           (this.d1*Math.Pow(this.d2 - 2, 2)*(this.d2 - 4));
                }

                return 0;
            }
        }

        /// <summary>
        /// Gets the skewness of the distribution.
        /// </summary>
        /// <value>The skewness of the distribution.</value>
        public double Skewness
        {
            get
            {
                if (this.d2 > 6)
                {
                    return ((2*this.d1 + this.d2 - 2)*Math.Sqrt(8*(this.d2 - 4)))/
                           ((this.d2 - 6)*Math.Sqrt(this.d1*(this.d1 + this.d2 - 2)));
                }

                return 0;
            }
        }

        /// <summary>
        /// Computes the probability mass function of the F-distribution at the specified postion x.
        /// </summary>
        /// <param name="x">The specified position x.</param>
        /// <returns>
        /// The probability mass function of the F-distribution at the specified postion x.
        /// </returns>
        public double ProbabilityMassFunction(double x)
        {
            return
                (Math.Sqrt((Math.Pow(this.d1*x, this.d1)*Math.Pow(this.d2, this.d2))/
                           (Math.Pow(this.d1*x + this.d2, this.d1 + this.d2))))/
                (x*SpecialFunctions.BetaFunction(this.d1/2.0, this.d2/2.0));
        }

        /// <summary>
        /// Computes the cumulative distribution function of the F-distribution at the specified postion x.
        /// </summary>
        /// <param name="x">The specified position x.</param>
        /// <returns>
        /// The cumulative distribution function of the F-distribution at the specified postion x.
        /// </returns>
        public double CumulativeDistributionFunction(double x)
        {
            return SpecialFunctions.RegularizedIncompleteBetaFunction((this.d1*x)/(this.d1*x + this.d2), this.d1/2.0,
                                                                      this.d2/2.0);
        }
    }
}